/**
 * ======================================================================
 * Created by Jack Jiang on 16-8-29.
 *
 * 【基本说明】：
 * 本类提供的是公开的数据发送方法（数据接收者仅限于本机在线用户，如果需要与MobileIMSDK-APP
 * 版互通的话，请调用im-server-sdk.js中的publicSendDataGlobal函数来实现）。
 * ======================================================================
 */


var logger = require('../j_conf/log').logger;
var ProtocalFactory = require('./protocal-factory');
var common = require('../j_common/common');


/**
 * 内部方法：发送一对一聊天通用数据.
 *
 * @param userProcessor
 * @param from_user_id
 * @param to_user_id
 * @param dataContent
 * @param typeu 应用层专用字段——用于应用层存放聊天、推送等场景下的消息类型。注意：此值为-1时表示未定义。
 *              MobileIMSDK_X框架中，本字段为保留字段，不参与框架的核心算法，专留用应用层自行定义和使用。
 * @param fnProcessOfflineMessage 非必须参数，用于消息没有在线发送成功时的离线消息处理回调
 * @returns {boolean}
 */
function sendData4(userProcessor, from_user_id, to_user_id, dataContent, typu, fnProcessOfflineMessage) {
    //return sendData3(userProcessor, from_user_id, to_user_id, dataContent, fnProcessOfflineMessage);
    return sendData2(userProcessor
        , ProtocalFactory.createCommonDataU(dataContent, from_user_id, to_user_id, typu), fnProcessOfflineMessage);
}

/**
 * 内部方法：发送一对一聊天通用数据.
 *
 * @param userProcessor
 * @param from_user_id
 * @param to_user_id
 * @param dataContent
 * @param fnProcessOfflineMessage 非必须参数，用于消息没有在线发送成功时的离线消息处理回调
 * @returns {boolean}
 */
function sendData3(userProcessor, from_user_id, to_user_id, dataContent, fnProcessOfflineMessage) {
    return sendData2(userProcessor
        , ProtocalFactory.createCommonData(dataContent, from_user_id, to_user_id), fnProcessOfflineMessage);//, fingerPrint));
}

/**
 * 内部方法：发送一对一聊天通用数据.
 *
 * @param userProcessor
 * @param p
 * @param fnProcessOfflineMessage 非必须参数，用于消息没有在线发送成功时的离线消息处理回调
 * @returns {boolean}
 */
function sendData2(userProcessor, p, fnProcessOfflineMessage) {
    if(p)
    {
        if(!("0" === p.to))
            return sendData1(userProcessor.getSession(p.to), p, fnProcessOfflineMessage);
        else
        {
            logger.warn("[SDKUtils]【注意】此Protocal对象中的接收方是服务器(user_id==0)（" +
                "而此方法本来就是由Server调用，自已发自已不可能！），数据发送没有继续！"+JSON.stringify(p));
            return false;
        }
    }
    else
        return false;
}

/**
 * 内部方法：发送一对一聊天通用数据。
 *
 * @param socket
 * @param p
 * @param fnProcessOfflineMessage 非必须参数，用于消息没有在线发送成功时的离线消息处理回调
 * @returns {boolean} true 表示实时在线发送成功，否则没有在线发送成功
 */
function sendData1(socket, p, fnProcessOfflineMessage){

    // 转义html，防止XSS攻击
    if(p)
        p.dataContent = common.htmlEncode(p.dataContent);

    // 要发送的目标用户的session不存在（不在线）
    if(!socket) {
        logger.info("[SDKUtils]toSocket==null >> id="+p.from+"的用户尝试发给客户端"+p.to
            +"的消息：str="+p.dataContent+"因接收方的id已不在线，此次实时发送没有继续(此消息应考虑作离线处理哦).");

        // @@ 没有成功实时发出的消息应考虑做离线处理哦！！
        if(fnProcessOfflineMessage){
            fnProcessOfflineMessage(p);
        }
        else{
            logger.warn("[SDKUtils] 很遗憾[CAUSE:!socket]，因未设置fnProcessOfflineMessage回调，消息 %s无法作离线回调处理，将被抛弃！"
                , JSON.stringify(p));
        }
    }
    else {
        // 要发送到的对方会话是正常连接着的
        if(socket.connected) {
            if (p) {
                //socket.emit(IMEvent.C_IMEVT_COMMON$DATA, p);
                //socket.send(p);
                socket.emit(p.type, p, function(data){
                    logger.warn("[SDKUtils]【socket.emit()的回调？】给客户端："+socket.username+"的数据 ->"+JSON.stringify(p)
                        +",进入回调了吗？回调的data="+JSON.stringify(data)+"。");
                }); // FIXME 这个方法是异步实现，那如何知道消息确实是被发出了？？这是个可能的隐患，有待深究！
                return true;
            }
            else{
                logger.error("[SDKUtils]给客户端："+socket.username+"的数据导然是空的! ->"+JSON.stringify(p)
                    +",发送失败！["+res.length+"](此条log应重点追查原因！).");
            }
        }
        else{
            logger.warn("[SDKUtils]toSession!=null但会话已经关闭 >> 客户端id="+p.from+"要发给客户端"+p.to
                +"的实时消息：str="+p.dataContent+"没有继续(此消息应考虑作离线处理哦).");

            // @@ 没有成功实时发出的消息应考虑做离线处理哦！！
            if(fnProcessOfflineMessage){
                fnProcessOfflineMessage(p);
            }
            else{
                logger.error("[SDKUtils] 很遗憾[CAUSE:!socket.connected]，因未设置fnProcessOfflineMessage" +
                    "回调，消息 %s 无法作离线回调处理，将被抛弃！", JSON.stringify(p));
            }
        }
    }

    return false;
}

/**
 * 公开方法：发送一对一聊天通用数据。
 *
 * @param socket
 * @param p
 * @param fnProcessOfflineMessage 非必须参数，用于消息没有在线发送成功时的离线消息处理回调
 * @returns {boolean} true 表示实时在线发送成功，否则没有在线发送成功
 * @type {sendData1}
 */
exports.sendData1 = sendData1;

/**
 * 发送一对一聊天通用数据。
 *
 * @param userProcessor
 * @param p
 * @param fnProcessOfflineMessage 非必须参数，用于消息没有在线发送成功时的离线消息处理回调
 * @returns {boolean}
 * @type {sendData2}
 */
exports.sendData2 = sendData2;

/**
 * 发送一对一聊天通用数据。
 *
 * @param userProcessor
 * @param from_user_id
 * @param to_user_id
 * @param dataContent
 * @param fnProcessOfflineMessage 非必须参数，用于消息没有在线发送成功时的离线消息处理回调
 * @returns {boolean}
 * @type {sendData3}
 */
exports.sendData3 = sendData3;

/**
 * 发送一对一聊天通用数据。
 *
 * @param userProcessor
 * @param from_user_id
 * @param to_user_id
 * @param dataContent
 * @param typeu 应用层专用字段——用于应用层存放聊天、推送等场景下的消息类型。注意：此值为-1时表示未定义。
 *              MobileIMSDK_X框架中，本字段为保留字段，不参与框架的核心算法，专留用应用层自行定义和使用。
 * @param fnProcessOfflineMessage 非必须参数，用于消息没有在线发送成功时的离线消息处理回调
 * @returns {boolean}
 * @type {sendData4}
 */
exports.sendData4 = sendData4;

/**
 * 因Socket.io的底层网络机制，很多时候不应立即关闭socket，否则将可能导致之前
 * 的事件尚未发送到客户端就将socket关闭了。建议在进行socket关闭时使用本通用方法
 * 进行layzy形式的关闭。
 *
 * @param socket 要被关闭的socket引用
 * @param descForLog debug信息
 */
exports.lazyCloseSocket = function (socket, descForLog) {
    setTimeout(function () {
        //logger.debug('[SDKUtils] 客户端：%s 因连接未获认证，马上关闭之... ', socket.id);
        logger.debug(descForLog);
        if (socket && socket.connected){
            // 删除认证信息（既然下面马上要断开连接，此处的删除认证其实是多此一举的保险措施：防止
            // 万一没有断开成功的话，至少清除了它的认证信息，这样这个socket即使逃过关闭也已处于
            // 非法状态而在下轮数据交互时被关闭）
            delete socket.userId;
            // 断开连接
            socket.disconnect(true);
        }
    }, 1000);// 不要立即关闭，延迟几秒以备客户端处理之前可能正在通信中的事件，此时间是个经验值，具体场景具体调整
};